package main

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"github.com/golang-jwt/jwt/v4"
	"gorm.io/gorm"
	"log"
	"net/http/httputil"
	"time"
)

// TODO: 日志组件

const (
	LoginSuccess                 = "登录成功"
	LoginFailureAccountNotExists = "账号不存在"
	RegisterSuccess              = "注册成功"
	RegisterFailureAccountExists = "账号已经存在"
	AccountFormatError           = "账号格式错误"
	PasswordFormatError          = "密码格式错误"
)

type User struct {
	//Id int64
	//Name string
	//Phone string
	Account string `json:"account" binding:"required"`
	Pwd     string `json:"pwd" binding:"required"`
}

var (
	GlobalDb    *gorm.DB
	GlobalRedis *redis.Client
	userService = UserService{}
)

func main() {
	initDatabase()
	router := setupRoutes()
	err := router.Run(":8080")
	if err != nil {
		return
	}
}

func initDatabase() {
	GlobalDb = InitMysql()
	RegisterTables(GlobalDb)
	GlobalRedis = InitRedis()
}

func setupRoutes() *gin.Engine {
	router := gin.Default()
	v1 := router.Group("/v1/public")
	{
		// {"account":"sss", "pwd": "xxx"}
		v1.POST("/register", registerController)
		// {"account":"sss", "pwd": "xxx"}
		v1.GET("/login", loginController)
	}
	v1.Use(inputOutLogMiddleware())
	return router
}

func inputOutLogMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		request, err := httputil.DumpRequest(ctx.Request, true)
		if err != nil {
			return
		}
		log.Println("request:", string(request))
		ctx.Next()
	}
}

// loginController 登录
func loginController(ctx *gin.Context) {
	user := User{}
	err := ctx.BindJSON(&user)
	if err != nil {
		errMsg := fmt.Sprint("参数错误:", err.Error())
		Result(FailWithMsg(errMsg), ctx)
		return
	}
	if !ValidateAccount(user.Account) {
		Result(FailWithMsg(AccountFormatError), ctx)
		return
	}
	if !ValidatePassword(user.Pwd) {
		Result(FailWithMsg(PasswordFormatError), ctx)
		return
	}
	u := &SysUser{Username: user.Account, Password: user.Pwd}
	sysUser, err := userService.Login(u)
	if err != nil {
		log.Println(err)
		Result(FailWithMsg(err.Error()), ctx)
		return
	}
	log.Printf("sysUser:%+v", sysUser)
	// 登录成功下发jwt
	token, err := createToken(sysUser)
	if err != nil {
		log.Println(err)
		Result(FailWithMsg(err.Error()), ctx)
		return
	}
	// 放入Redis，之后的请求会从Redis里对比jwt
	err = GlobalRedis.Set(context.Background(), sysUser.UUID.String(), token, 0).Err()
	if err != nil {
		Result(FailWithMsg(err.Error()), ctx)
		return
	}
	Result(SuccessWithData(token, LoginSuccess), ctx)
}

// createToken 创建jwt
func createToken(sysUser *SysUser) (string, error) {
	// Create the claims
	claims := MyCustomClaims{
		BaseClaims{
			UUID:     sysUser.UUID,
			ID:       sysUser.ID,
			Username: sysUser.Username,
		},
		jwt.RegisteredClaims{
			// A usual scenario is to set the expiration time relative to the current time
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			NotBefore: jwt.NewNumericDate(time.Now()),
			Issuer:    "test",
			Subject:   "somebody",
			ID:        "1",
			Audience:  []string{"somebody_else"},
		},
	}
	rywJwt := RywJwt{SignKey: []byte(JWTSignKey)}
	return rywJwt.CreateToken(claims)
}

// registerController 注册
func registerController(ctx *gin.Context) {
	// 验证账号密码格式
	user := User{}
	err := ctx.BindJSON(&user)
	if err != nil {
		errMsg := fmt.Sprint("参数错误:", err.Error())
		Result(FailWithMsg(errMsg), ctx)
		return
	}
	if !ValidateAccount(user.Account) {
		Result(FailWithMsg(AccountFormatError), ctx)
		return
	}
	if !ValidatePassword(user.Pwd) {
		Result(FailWithMsg(PasswordFormatError), ctx)
		return
	}
	// 先尝试登录
	u := &SysUser{Username: user.Account, Password: user.Pwd}
	_, err = userService.Login(u)
	if err == nil {
		log.Println(err)
		Result(FailWithMsg(RegisterFailureAccountExists), ctx)
		return
	}
	// 注册
	_, err = userService.Register(u)
	if err != nil {
		log.Println(err)
		Result(FailWithMsg(err.Error()), ctx)
		return
	}
	Result(SuccessWithMsg(RegisterSuccess), ctx)
}
